Script Modules - Script Engine

Each Script Module has one instance of a Qt Script Engine. A Script Module can contain multiple script files written in ECMAScript language (JavaScript). When the Script Module starts, all script files are executed (evaluated) in the Script Engine in the same order as listed in the Scripting Interface. As long as the Script Module is running, the Script Engine is running. Script Module logics should be coded in these files.

When the Script Module starts, it will call the main() function defined in the Script Engine. Setup code for this Script Module should be placed in this function, such as adding menu items, registering for events, launching extra web views, locking GUI, etc.

When the Script Module stops, it will call the cleanUp() function defined in the Script Engine. Clean-up code should be placed here, such as removing menu items, unregistering for events, closing any web view created by this Script Module, unlocking GUI locked by this Script Module, etc.

Changes made to the Script Engine after it has started DO NOT take effect until it has been stopped and started again. The Run File button in the Scripting Interface can be used to make changes to the Script Engine during run-time as described in a previous section.

The Script Engine has access to the IPC and to custom interfaces in web views as described in later sections.

IPC Access

A main part of the Packet Tracer model is the PT engine and GUI. They can be accessed via IPC calls the same way ExApps can access them. Although they are not considered IPC calls since Script Modules are in the same process as PT, we will still use the term IPC to refer to the API that PT exposes. Each Script Module has a set of security privileges that it requests before it can make any IPC calls to PT.

The complete IPC API reference is located at the Packet Tracer Community. They are declared in .pki files. Each file contains the declaration of an interface, data, or PDU. An interface is a class that associates to a PT core object that the Script Module can manipulate (make calls and receive events). Data is a data structure that is returned to the Script Module. Once returned, the data does not associate to any object in PT core. A PDU is a special type of data that describes a frame, packet, or other PDU types that PT simulates.

IPC calls can be made from the Script Engine or in web views. The main IPC object is ipc, which is the same as the IPCFrameWork in C++ IPC Framework, or the IPC interface in CMainParser.pki.

Direct IPC calls

Now with better integration, we can do:

var ip = ipc.network().getDevice(deviceName).getPort(portName).getIpAddress();

Object Manipulation

We can also assign objects to variables, IPC calls return objects, and pass objects as arguments into IPC calls:

    var device = ipc.network().getDevice(deviceName);
    var port = device.getPort("FastEthernet0/0");
    port.setPower(true);
    port.setIpSubnetMask(ip1, mask1);
    ...
    var otherPort = port.getLink().getOtherPortConnectedTo(port);
    otherPort.setPower(true);
    otherPort.setIpSubnetMask(ip2, mask2);

Events

Events use the same process as in ExApps. If the Script Module wants to be notified when an event in PT core happens, register to it first. But with Script Module, events are handled easier and more directly:

    // register an event to callback a method of an object
    port.registerEvent("ipChanged", obj, obj.callbackFunc);

    // register an event to callback a global function
    port.registerEvent("powerChanged", null, globalFunc);

The callback function should always have the same function prototype:

    callbackFunc = function(src, args) { ... }

The src argument is always an object that has three members: className, objectUuid, and eventName. They describe the source of the event. The args argument is an object, but depending on the event, it would have different members. The members are the variable names in the pki event definition.

For example, the IpChanged event for HostPort.pki

    event: ipChanged(ip newIp, ip newMask, ip oldIp, ip oldMask) - PrivGetNetwork;

In the Script Module callback function:

Argument and members | Values
--- | ---
src.className | "HostPort"
src.objectUuid | port's object UUID
src.eventName | "ipChanged"
args.newIp | port's new IP
args.newMask | port's new subnet mask
args.oldIp | port's old IP
args.oldMask | port's old subnet mask

Delegates

A new type of communication is also added to the IPC just for Script Modules -- delegates. When there is functionality that PT wants Script Modules to implement or supply, it will send that request to its delegates. Delegates are like events, except they return values back to the one sending it. They are added in pki files:

    delegate#one: bool processData(QString data, ip srcIp, int srcPort) - PrivChangeNetwork;

    delegate#all: string getCustomInfo(Device device) - PrivMiscGui;

The delegate#one means the delegating source expects only one delegate and it will only execute and take the return value of the first delegate registered to it. The delegate#all means the delegating source will execute and take the return values of all delegates registered to it. The delegating source will define how the returned value of all delegates will be used in the delegate definition in pki file.

In Script Modules, registering for them is similar to registering for events. However, the callback functions need to return the same type as what the delegate definition expects.

    var process = ....;
    process.registerDelegate("processData", this, this.callbackFunc);

    var quickDeviceInfo = ....;
    quickDeviceInfo.registerDelegate("getCustomInfo", null, globalFunc);

With delegates, we are able to extend PT functionality in the core, such as adding new protocols, reacting to GUI events, and suppressing default behavior (future feature).

Event and Delegate Limitations

There is one limitation to the events and delegates -- the registering and callback functions must be in the script engine, they cannot be in web views. This is intended as web views are not as persistent as the script engine, and it breaks the MVC design pattern.

Messages

Script Modules and ExApps can send messages to other Script Modules and other ExApps. These IPC calls use the Script Module or ExApp ID as the destination.

    // send message to Script Module or ExApps with specified ID
    ipc.ipcManager().sendMessageTo("com.yourcompany.stpTree", "message");

    // send message to Script Module or ExApp with specified ID and instance ID
    ipc.ipcManager().sendMessageToInstance("{12345678-....}", "message");

    // send message to all Script Modules and ExApps
    ipc.ipcManager().sendMessageToAll("message");

Script Modules cannot run multiple instances like ExApps do, and so the sendMessageToInstance() call is not intended for Script Modules.

In order to receive messages, the Script Module needs to register to the messageReceived event.

    ipc.ipcManager().thisInstance().registerEvent("messageReceived", null, onMessage);

    onMessage = function(src, args)
    {
        doSomething(args.msg);
    }